/**
 * 
 */
package com.game.controller;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;

import com.game.entity.Asteroid;
import com.game.entity.Bullet;
import com.game.entity.Ship;

/**
 * @author gustavo
 * 
 */
public class Asteroids extends Applet implements Runnable, KeyListener {

	// Loop game
	Thread gameLoop;

	// Usar isso como buffer secundário
	BufferedImage backBuffer;

	// Desenha os objetos do backBuffer
	Graphics2D g2D;

	// desenhar os formatos das caixas
	boolean showBounds = false;

	// Cria um arrey de asteroids
	int ASTEROIDS = 20;
	Asteroid[] ast = new Asteroid[ASTEROIDS];

	// Cria o array das balas
	int BULLETS = 10;
	Bullet[] bullet = new Bullet[BULLETS];
	int currentBullet = 0;

	// Cria o jogador
	Ship ship = new Ship();

	// Cria a transformação identidade(0,0)
	AffineTransform identity = new AffineTransform();

	// Cria gerador randomico
	Random rand = new Random();

	@Override
	public void init() {
		super.init();
		setSize(800, 600);
		// Criando buffer
		backBuffer = new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB);
		g2D = backBuffer.createGraphics();

		// Definindo a nave
		ship.setX(320);
		ship.setY(240);

		// Definindo as balas
		for (int n = 0; n < BULLETS; n++) {
			bullet[n] = new Bullet();
		}

		// Criando os asteroids
		for (int n = 0; n < ASTEROIDS; n++) {
			ast[n] = new Asteroid();
			ast[n].setRotationVelocity(rand.nextInt(3) + 1);
			ast[n].setX((double) rand.nextInt(600) + 20);
			ast[n].setY((double) rand.nextInt(440) + 20);
			ast[n].setMoveAngle(rand.nextInt(360));
			double ang = ast[n].getMoveAngle() - 90;
			ast[n].setVelX(calcAngleMoveX(ang));
			ast[n].setVelY(calcAngleMoveY(ang));
		}

		//
		addKeyListener(this);
	}

	@Override
	public void update(Graphics g) {

		//
		g2D.setTransform(identity);

		// apaga backgroud
		g2D.setPaint(Color.BLACK);
		g2D.fillRect(0, 0, getSize().width, getSize().height);

		// exibe algumas informações de estatus
		g2D.setColor(Color.WHITE);
		g2D.drawString(
				"Nave :" + Math.round(ship.getX()) + ","
						+ Math.round(ship.getX()), 5, 10);
		g2D.drawString("Move Angle :" + Math.round(ship.getMoveAngle() + 90),
				5, 25);
		g2D.drawString("Face Angle :" + Math.round(ship.getFaceAngel()), 5, 40);

		// Desenha grafico do jogo
		drawShip();
		drawBullets();
		drawAsteroids();

		// Atualiza a tela
		paint(g);

	}

	private void drawAsteroids() {
		for (int n = 0; n < ASTEROIDS; n++) {
			if (ast[n].isAlive()) {
				g2D.setTransform(identity);
				g2D.translate(ast[n].getX(), ast[n].getY());
				g2D.rotate(Math.toRadians(ast[n].getMoveAngle()));
				g2D.setColor(Color.DARK_GRAY);
				g2D.fill(ast[n].getShape());
			}

		}

	}

	private void drawBullets() {
		for (int n = 0; n < BULLETS; n++) {
			// A bala esta sendo usada ?
			if (bullet[n].isAlive()) {
				// Desenhar a bala
				g2D.setTransform(identity);
				g2D.translate(bullet[n].getX(), bullet[n].getY());
				g2D.setColor(Color.MAGENTA);
				g2D.draw(bullet[n].getShape());
			}
		}

	}

	private void drawShip() {
		g2D.setTransform(identity);
		g2D.translate(ship.getX(), ship.getX());
		g2D.rotate(Math.toRadians(ship.getFaceAngel()));
		g2D.setColor(Color.ORANGE);
		g2D.fill(ship.getShape());
	}

	@Override
	public void paint(Graphics g) {
		g.drawImage(backBuffer, 0, 0, this);
	}

	private double calcAngleMoveY(double angle) {
		return (Math.sin(angle*Math.PI/180));
	}

	private double calcAngleMoveX(double angle) {
		// TODO Auto-generated method stub
		return (Math.cos(angle*Math.PI/180));
	}

	/**
	 * @throws HeadlessException
	 */
	public Asteroids() throws HeadlessException {
		// TODO Auto-generated constructor stub
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
	 */
	@Override
	public void keyPressed(KeyEvent k) {
		int keyCode = k.getKeyCode();

		switch (keyCode) {
		case KeyEvent.VK_LEFT:
			// left arrow rotates ship left 5 degrees
			ship.incFaceAngel(-5);
			if (ship.getFaceAngel() < 0)
				ship.setFaceAngel(360 - 5);
			break;
		case KeyEvent.VK_RIGHT:
			// right arrow rotates ship left 5 degrees
			ship.incFaceAngel(5);
			if (ship.getFaceAngel() > 360)
				ship.setFaceAngel(5);
			break;

		case KeyEvent.VK_UP:
			// left arrow rotates ship left 5 degrees
			ship.setMoveAngle(ship.getFaceAngel() - 90);
			ship.incVelX(calcAngleMoveX(ship.getMoveAngle()) * 0.1);
			ship.incVelY(calcAngleMoveY(ship.getMoveAngle()) * 0.1);
			break;
		case KeyEvent.VK_CONTROL:
		case KeyEvent.VK_ENTER:
		case KeyEvent.VK_SPACE:
			// fire bullets
			currentBullet++;
			if (currentBullet > BULLETS - 1)
				currentBullet = 0;
			bullet[currentBullet].setAlive(true);

			// point bullet in same direction ship is facing
			bullet[currentBullet].setVelX(ship.getX());
			bullet[currentBullet].setVelY(ship.getY());
			bullet[currentBullet].setMoveAngle(ship.getFaceAngel() - 90);

			// fire bullet at angle of the ship
			double angle = bullet[currentBullet].getMoveAngle();
			double svx = ship.getVelX();
			double svy = ship.getVelY();
			bullet[currentBullet].setVelX(svx + calcAngleMoveX(angle) * 2);
			bullet[currentBullet].setVelY(svy + calcAngleMoveY(angle) * 2);

			break;

		default:
			break;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
	 */
	@Override
	public void keyReleased(KeyEvent arg0) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
	 */
	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		Thread t = Thread.currentThread();

		while (t == gameLoop) {
			try {
				gameUpdate();
				// target framerate is 50 fps
				Thread.sleep(20);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			repaint();
		}
	}

	@Override
	public void stop() {
		// kill the gameloop thread
		gameLoop = null;

	}

	private void gameUpdate() {
		updateShip();
		updateBullets();
		updateAsteroids();
		checkcollisions();
	}

	private void checkcollisions() {
		// iterate througth the asteroids array
		for (int m = 0; m < ASTEROIDS; m++) {
			// is this asteroid being used ?
			if (ast[m].isAlive()) {
				// check for colliison with bullet
				for (int n = 0; n < BULLETS; n++) {
					// is this bullets being used?
					if (bullet[n].isAlive()) {
						// perform the collision test
						if (ast[n].getBounds().contains(bullet[n].getX(),
								bullet[n].getY())) {
							bullet[n].setAlive(false);
							ast[m].setAlive(false);
							continue;
						}
					}
				}
				// check for collision with ship
				if (ast[m].getBounds().intersects(ship.getBounds())) {
					ast[m].setAlive(false);
					ship.setX(320);
					ship.setY(240);
					ship.setFaceAngel(0);
					ship.setVelX(0);
					ship.setVelY(0);
					continue;
				}
			}

		}
	}

	private void updateAsteroids() {
		// move and rotate the asteroids
		for (int n = 0; n < ASTEROIDS; n++) {
			Asteroid asteroid = ast[n];
			// is this asteroid being used?
			if (asteroid.isAlive()) {
				// update the asteroid X value
				asteroid.incX(asteroid.getVelX());

				// wrap the asteroid at screen edges
				if (asteroid.getX() < -20)
					asteroid.setX(getSize().width + 20);
				else if (asteroid.getX() > getSize().width + 20) {
					asteroid.setX(-20);
				}

				// update the asteroid Y value
				asteroid.incY(asteroid.getVelY());

				// wrap the asteroid at screen edges
				if (asteroid.getY() < -20)
					asteroid.setX(getSize().height + 20);
				else if (asteroid.getX() > getSize().height + 20) {
					asteroid.setY(-20);
				}

				// update the asteroid rotation
				asteroid.incMoveAngle(asteroid.getRotationVelocity());

				// Keep the angle within 0-359 degrees
				if (asteroid.getMoveAngle() < 0) {
					asteroid.setMoveAngle(360 - asteroid.getRotationVelocity());
				} else if (asteroid.getMoveAngle() > 360) {
					asteroid.setMoveAngle(asteroid.getRotationVelocity());
				}

			}

		}

	}

	private void updateBullets() {
		// move each of the bullets
		for (int n = 0; n < BULLETS; n++) {
			// is this bullet begins used?
			if (bullet[n].isAlive()) {
				// update bullet`s X positions
				bullet[n].incX(bullet[n].getVelX());

				// bullet disappears at left/right edge
				if (bullet[n].getX() < 0 || bullet[n].getX() > getSize().width) {
					bullet[n].setAlive(false);
				}

				// update bullets Y position
				bullet[n].incY(bullet[n].getVelY());

				// bullet disappears at top/buttum edge
				if (bullet[n].getY() < 0 || bullet[n].getY() > getSize().height) {
					bullet[n].setAlive(false);
				}
			}
		}

	}

	private void updateShip() {
		// update ships X position
		ship.incX(ship.getVelX());

		// Wrap around leff/right
		if (ship.getX() < -10) {
			ship.setX(getSize().width + 10);
		} else if (ship.getX() > getSize().width + 10) {
			ship.setX(-10);

		}

		// update ship Y position
		ship.incY(ship.getVelY());

		// Wrap around top/button
		if (ship.getY() < -10) {
			ship.setX(getSize().height + 10);
		} else if (ship.getX() > getSize().height + 10) {
			ship.setX(-10);

		}
	}

	@Override
	public void start() {
		gameLoop = new Thread(this);
		gameLoop.start();
	}

}
